package com.kg.tombs.service;

import com.kg.tombs.db.MongoVerticle;
import com.kg.tombs.utils.PinyinUtils;
import io.vertx.core.Future;
import io.vertx.core.Vertx;
import io.vertx.core.json.JsonArray;
import io.vertx.core.json.JsonObject;
import lombok.extern.slf4j.Slf4j;
import org.neo4j.driver.*;
import org.neo4j.driver.Record;
import org.neo4j.driver.async.AsyncSession;
import org.neo4j.driver.exceptions.ClientException;
import org.neo4j.driver.internal.value.*;
import org.neo4j.driver.types.Node;
import org.neo4j.driver.types.Relationship;

import java.util.*;
import java.util.stream.Collectors;

@Slf4j
public class Neo4jService {

    private final String neo4jUri = "bolt://localhost:7687";
    private final String neo4jUser = "neo4j";
    private final String neo4jPassword = "123qweasdzxc";

    // 黑名单：包含这些词的字段不参与维度识别（也不建关系）
    private static final List<String> EXCLUDE_KEYWORDS = Arrays.asList("_id", "序号", "ID", "编号", "index", "子类", "来源类别_原始","来源类别","_instance_id");
    private static final String DIMENSION_LABEL = "Dimension";
    private static final String ATTRIBUTE_LABEL = "Attribute";
    private static final String RELATIONSHIP_TYPE = "HAS_ATTRIBUTE";
    private static final Integer MAX_RECORDS_PER_QUERY = 500;

    private static final double UNIQUENESS_THRESHOLD = 0.01;

    private final Driver driver;

    private final Vertx vertx;

    private final MongoVerticle mongoVerticle;

    public Neo4jService(Vertx vertx, MongoVerticle mongoVerticle) {
        this.vertx = vertx;
        this.mongoVerticle = mongoVerticle;
        this.driver = GraphDatabase.driver(this.neo4jUri, AuthTokens.basic(this.neo4jUser, this.neo4jPassword));
    }

    public Future<JsonObject> getFullGraph(String collection) {
        String dataSet = PinyinUtils.toValidDatabaseName(collection);
        return vertx.executeBlocking(promise -> {
            try (Session session = driver.session()) {
                Result result = session.run(
                        "MATCH (d1)-[r:HAS_ATTRIBUTE]->(d2) WHERE d1.dataSet = $dataSet AND d2.dataSet = $dataSet RETURN d1, r, d2 " ,
//                                + "LIMIT 1000",
                        Values.parameters("dataSet", dataSet)
                );

                // 使用Map来存储唯一节点
                Map<String, JsonObject> nodesMap = new HashMap<>();
                JsonArray edges = new JsonArray();

                while (result.hasNext()) {
                    Record record = result.next();

                    // 获取节点和关系信息
                    Node d1Node = record.get("d1").asNode();
                    Node d2Node = record.get("d2").asNode();
                    Relationship relation = record.get("r").asRelationship();



                    // 处理d1节点
                    String d1Id = "dim_" + d1Node.id();
                    if (!nodesMap.containsKey(d1Id)) {
                        JsonObject d1NodeData = new JsonObject();
                        d1NodeData.put("id", d1Id);
                        d1NodeData.put("type", "dimension");

                        d1NodeData.put("color", d1Node.get("color").asString());

                        // 使用节点的value作为label
                        String label = "Unknown";
                        if (d1Node.containsKey("value")) {
                            label = d1Node.get("value").asString();
                        } else {
                            // 备用方案：使用第一个标签作为label
                            Iterator<String> labelIterator = d1Node.labels().iterator();
                            if (labelIterator.hasNext()) {
                                label = labelIterator.next();
                            }
                        }
                        d1NodeData.put("label", label);

                        JsonObject properties = new JsonObject();
                        if (d1Node.containsKey("value")) {
                            properties.put("value", d1Node.get("value").asString());
                        }
                        for (Map.Entry<String, Object> prop : d1Node.asMap().entrySet()) {
                            properties.put(prop.getKey(), prop.getValue().toString());
                        }
                        d1NodeData.put("properties", properties);
                        nodesMap.put(d1Id, d1NodeData);
                    }

                    // 处理d2节点
                    String d2Id = "dim_" + d2Node.id();
                    if (!nodesMap.containsKey(d2Id)) {
                        JsonObject d2NodeData = new JsonObject();
                        d2NodeData.put("id", d2Id);
                        d2NodeData.put("type", "dimension");

                        d2NodeData.put("color", d2Node.get("color").asString());

                        // 使用节点的value作为label
                        String label = "Unknown";
                        if (d2Node.containsKey("value")) {
                            label = d2Node.get("value").asString();
                        } else {
                            // 备用方案：使用第一个标签作为label
                            Iterator<String> labelIterator = d2Node.labels().iterator();
                            if (labelIterator.hasNext()) {
                                label = labelIterator.next();
                            }
                        }
                        d2NodeData.put("label", label);

                        JsonObject properties = new JsonObject();
                        if (d2Node.containsKey("value")) {
                            properties.put("value", d2Node.get("value").asString());
                        }
                        for (Map.Entry<String, Object> prop : d2Node.asMap().entrySet()) {
                            properties.put(prop.getKey(), prop.getValue().toString());
                        }
                        d2NodeData.put("properties", properties);
                        nodesMap.put(d2Id, d2NodeData);
                    }

                    // 处理关系（边）
                    JsonObject edge = new JsonObject();
                    edge.put("source", d1Id);
                    edge.put("target", d2Id);
                    edge.put("type", relation.type());
                    edge.put("linkDistance",relation.get("linkDistance"));
                    edges.add(sanitizeNeo4jData(edge));
                }

                // 构建最终的图数据结构
                JsonObject graphData = new JsonObject();
                graphData.put("nodes", new JsonArray(new ArrayList<>(nodesMap.values())));
                graphData.put("edges", edges);

                log.info("成功查询数据集 {} 的图数据，共 {} 个节点，{} 条边",
                        dataSet, nodesMap.size(), edges.size());
                promise.complete(graphData);
            } catch (Exception e) {
                log.error("查询数据集 {} 图数据时出错", dataSet, e);
                promise.fail(e);
            }
        });
    }

    /**
     * 清除指定数据集的所有数据
     */
    public Future<Void> clearDataSet(String dataSet) {
        return vertx.executeBlocking(promise -> {
            try (Session session = driver.session()) {
                // 使用 DETACH DELETE 一次性删除Record节点及其所有关系
                session.run(
                        "MATCH (r:Record {dataSet: $dataSet}) " +
                                "DETACH DELETE r",
                        Values.parameters("dataSet", dataSet)
                );

                // 删除相关的维度节点
                session.run(
                        "MATCH (d) WHERE d.dataSet = $dataSet " +
                                "DETACH DELETE d",
                        Values.parameters("dataSet", dataSet)
                );

                log.info("成功清除数据集 {} 的所有数据", dataSet);
                promise.complete();
            } catch (Exception e) {
                log.error("清除数据集 {} 数据时出错", dataSet, e);
                promise.fail(e);
            }
        });
    }

    /**
     * 关闭Neo4j驱动
     */
    public void close() {
        driver.close();
    }

    public Future<Void> save(List<JsonObject> records, String dataSet) {
        return vertx.executeBlocking(promise -> {
            Set<String> analyzeDimensionFields = calculateFieldUniquenessRatioSync(records);
            log.info("分析维度字段：" + analyzeDimensionFields);
            JsonObject first = records.getFirst();
            Set<String> fieldNames = first.fieldNames();
            Map<String, String> colorMap = generateRandomColor(fieldNames);
            try (Session session = driver.session()) {
                // 使用同步事务处理
                session.executeWrite(tx -> {

                    List<String> dim = new ArrayList<>();
                    List<String> attr = new ArrayList<>();
                    Map<Integer,Integer> relationship = new HashMap<>();

                    for (JsonObject item : records) {
                        JsonObject filteredRecord = new JsonObject();
                        for (String fieldName : fieldNames) {
                            if (!EXCLUDE_KEYWORDS.contains(fieldName)) {
                                filteredRecord.put(fieldName, item.getValue(fieldName));
                            }
                        }
                        List<String> dimFields = new ArrayList<>();
                        List<String> attrFields = new ArrayList<>();

                        for (String key : filteredRecord.fieldNames()) {
                            if (analyzeDimensionFields.contains(key)) {
                                dimFields.add(key);
                            } else {
                                attrFields.add(key);
                            }
                        }

                        // 创建维度节点（MERGE）
                        for (String dimField : dimFields) {
                            Object dimValue = filteredRecord.getValue(dimField);
                            if (isEmpty(dimValue)) {
                                continue;
                            }
                            String cypherDim = "MERGE (d:" + DIMENSION_LABEL + " {fieldName: $field, value: $value, dataSet: $dataSet, color: '#f36924'})";
                            tx.run(cypherDim, Values.parameters("field", toStringSafe(dimValue), "value", toStringSafe(dimValue), "dataSet", dataSet));
                        }

                        for (String attrField : attrFields) {
                            Object attrValue = filteredRecord.getValue(attrField);
                            if (isEmpty(attrValue)) {
                                continue;
                            }
                            String cypherAttr = "MERGE (a:" + ATTRIBUTE_LABEL + " {fieldName: $field, value: $value, dataSet: $dataSet, color: $color})";
                            tx.run(cypherAttr, Values.parameters("field", toStringSafe(attrValue), "value", toStringSafe(attrValue), "dataSet", dataSet, "color", colorMap.get(attrField)));
                        }

                        final String cypherRel = """
                            MATCH (d:Dimension {fieldName: $dimField, value: $dimValue, dataSet: $dataSet})
                            MATCH (a:Attribute {fieldName: $attrField, value: $attrValue, dataSet: $dataSet})
                            MERGE (d)-[r:HAS_ATTRIBUTE]->(a)
                            SET r.linkDistance=$linkDistance
                            """;

                        // 建立关系：每个维度 -> 每个普通属性
                        for (String dimField : dimFields) {
                            Object dimValue = filteredRecord.getValue(dimField);
                            int linkDistance = 300;
                            for (String attrField : attrFields) {
                                Object attrValue = filteredRecord.getValue(attrField);
                                if(isEmpty(attrValue) || isEmpty(dimValue)){
                                    continue;
                                }
                                tx.run(cypherRel,
                                        Values.parameters(
                                                "dimField", toStringSafe(dimValue),
                                                "dimValue", toStringSafe(dimValue),
                                                "dataSet", dataSet,
                                                "attrField", toStringSafe(attrValue),
                                                "attrValue", toStringSafe(attrValue),
                                                "linkDistance", linkDistance
                                        ));
                                linkDistance += 200;
                            }
                        }
                    }
                    return null;
                });
                promise.complete();
            } catch (Exception e) {
                log.error("保存数据集 {} 数据时出错", dataSet, e);
                promise.fail(e);
            }
        });
    }

    public Future<Void> insert(String collection, List<JsonObject> records, String dataSet) {
        return vertx.executeBlocking(promise -> {
            try (Session session = driver.session()) {
                // 使用单个事务处理所有记录
                Transaction tx = session.beginTransaction();
                try {
                    Set<String> analyzeDimensionFields = calculateFieldUniquenessRatioSync(records);
                    log.info("分析维度字段：" + analyzeDimensionFields);

                    for (int i = 0; i < records.size(); i++) {
                        JsonObject record = records.get(i);
                        insertRecord(tx, record, "Record_" + i, analyzeDimensionFields, dataSet);
                    }
                    tx.commit(); // 一次性提交所有操作
                    log.info("成功插入 {} 条记录到数据库: {}", records.size(), collection);
                    promise.complete();
                } catch (Exception e) {
                    tx.rollback(); // 出错时回滚事务
                    throw e;
                }
            } catch (Exception e) {
                log.error("插入数据时出错", e);
                promise.fail(e);
            }
        });
    }

    /**
     * 插入单条记录
     * @param tx 事务
     * @param record 记录
     * @param recordId 记录ID
     * @param dimensionFields 维度字段
     * @param dataSet 数据集
     */
    private static void insertRecord(Transaction tx, JsonObject record, String recordId, Set<String> dimensionFields, String dataSet) {
        // 收集所有需要创建的节点信息
        List<Map<String, Object>> nodesToCreate = new ArrayList<>();

        // 收集所有需要创建的关系信息
        List<Map<String, Object>> relationshipsToCreate = new ArrayList<>();
        // 为每个非排除字段收集节点信息
        for (String field : record.fieldNames()) {
            if (shouldExcludeField(field)) {
                continue;
            }

            String value = getString(record, field);
            if (value == null || value.isEmpty() || "不详".equals(value) || "遮挡".equals(value)) {
                continue;
            }

            Map<String, Object> nodeInfo = new HashMap<>();
            nodeInfo.put("field", field);
            nodeInfo.put("value", value);
            nodesToCreate.add(nodeInfo);
        }
        for (Map<String, Object> nodeInfo : nodesToCreate) {
            String field = (String) nodeInfo.get("field");
            String value = (String) nodeInfo.get("value");
            tx.run(
                    "MERGE (f:Field:`" + value + "` {value: $value, dataSet: $dataSet, fieldName: $fieldName})",
                    Values.parameters("value", value, "dataSet", dataSet, "fieldName", field)
            );
        }
        // 收集维度字段到其他字段的关系信息
        for (String dimField : dimensionFields) {
            if (!record.containsKey(dimField)) continue;
            String dimValue = getString(record, dimField);
            if (dimValue == null || dimValue.isEmpty() || "不详".equals(dimValue) || "遮挡".equals(dimValue)) {
                continue;
            }

            for (Map<String, Object> nodeInfo : nodesToCreate) {
                String otherField = (String) nodeInfo.get("field");
                String otherValue = (String) nodeInfo.get("value");

                // 跳过排除字段、维度字段本身以及其他维度字段
                if (shouldExcludeField(otherField) || dimensionFields.contains(otherField) ||
                        otherField.equals(dimField)) {
                    continue;
                }

                Map<String, Object> relInfo = new HashMap<>();
                relInfo.put("dimValue", dimValue);
                relInfo.put("dimField", dimField);
                relInfo.put("otherValue", otherValue);
                relInfo.put("otherField", otherField);
                relationshipsToCreate.add(relInfo);
            }
        }

        // 在执行查询前验证数据
        if (relationshipsToCreate == null || relationshipsToCreate.isEmpty()) {
            // 处理空数据情况，避免执行无效查询
            return;
        }
        // 验证每个关系对象的必需字段
        for (Object rel : relationshipsToCreate) {
            if (rel instanceof Map) {
                Map<String, Object> relMap = (Map<String, Object>) rel;
                if (!relMap.containsKey("dimValue") || !relMap.containsKey("otherValue") ||
                    !relMap.containsKey("dimField") || !relMap.containsKey("otherField")) {
                    throw new IllegalArgumentException("关系数据缺少必要字段");
                }
            }
        }
        // 将多个关系合并为一个Cypher查询，减少数据库交互次数
        StringBuilder queryBuilder = new StringBuilder();
        Map<String, Object> parameters = new HashMap<>();
        queryBuilder.append("UNWIND $rels as rel ");
        queryBuilder.append("MERGE (d:Dimension {value: rel.dimValue, dataSet: $dataSet, fieldName: rel.dimField}) ");
        queryBuilder.append("MERGE (f:Fact {value: rel.otherValue, dataSet: $dataSet, fieldName: rel.otherField}) ");
        queryBuilder.append("MERGE (d)-[:HAS]->(f)");
        parameters.put("rels", relationshipsToCreate);
        parameters.put("dataSet", dataSet);
        tx.run(queryBuilder.toString(), parameters);
    }


    /**
     * 判断字段是否应排除（不参与维度识别，也不建关系）
     */
    private static boolean shouldExcludeField(String fieldName) {
        String lower = fieldName.toLowerCase();
        return EXCLUDE_KEYWORDS.stream().anyMatch(lower::contains);
    }

    private static String getString(JsonObject obj, String key) {
        if (!obj.containsKey(key) || obj.getString(key).isEmpty()) return null;
        return obj.getString(key).trim();
    }

    private Set<String> calculateFieldUniquenessRatioSync(List<JsonObject> records) {
        if (records.isEmpty()) {
            return new HashSet<>();
        }

        Set<String> dimensions = new HashSet<>();

        Map<String, Set<String>> fieldValueSet = new HashMap<>();
        Map<String, Integer> fieldValidCount = new HashMap<>();

        // 收集每个字段的唯一值和有效值总数
        for (JsonObject record : records) {
            for (String field : record.fieldNames()) {
                if (shouldExcludeField(field)) {
                    continue;
                }

                String value = getString(record, field);
                // 排除空值、"无"、"不详"等无效值
                if (value == null || value.isEmpty() || "无".equals(value) ||
                        "不详".equals(value) || "遮挡".equals(value)) {
                    continue;
                }

                // 添加到唯一值集合
                fieldValueSet.computeIfAbsent(field, k -> new HashSet<>()).add(value);
                // 增加有效值计数
                fieldValidCount.merge(field, 1, Integer::sum);
            }
        }

        for (Map.Entry<String, Set<String>> entry : fieldValueSet.entrySet()) {
            String field = entry.getKey();
            int uniqueCount = entry.getValue().size();
            int validCount = fieldValidCount.getOrDefault(field, 0);

            if (validCount > 0) {
                double ratio = (double) uniqueCount / validCount;
                if(ratio <= UNIQUENESS_THRESHOLD && uniqueCount > 1){
                    dimensions.add(field);
                }
//                log.info("字段 '{}' 的去重值占比: {}/{} = {}", field, uniqueCount, validCount, ratio);
            }
        }
        return dimensions;
    }

    private static String toStringSafe(Object obj) {
        if (obj == null) return "null";
        if (obj instanceof String) return (String) obj;
        return obj.toString();
    }

    private Map<String,String> generateRandomColor(Set<String> attrFields) {
        Map<String,String> colors = new HashMap<>();
        for (String attrField : attrFields) {
            int r = (int) (Math.random() * 256);
            int g = (int) (Math.random() * 256);
            int b = (int) (Math.random() * 256);
            String color = String.format("#%02x%02x%02x", r, g, b);
            colors.put(attrField, color);
        }
        return colors;
    }

    private JsonObject sanitizeNeo4jData(JsonObject data) {
        JsonObject sanitized = new JsonObject();
        for (String key : data.fieldNames()) {
            Object value = data.getValue(key);
            if (value instanceof IntegerValue) {
                sanitized.put(key, ((IntegerValue) value).asInt());
            } else if (value instanceof StringValue) {
                sanitized.put(key, ((StringValue) value).asString());
            } else if (value instanceof FloatValue) {
                sanitized.put(key, ((FloatValue) value).asDouble());
            } else if (value instanceof BooleanValue) {
                sanitized.put(key, ((BooleanValue) value).asBoolean());
            } else if (value instanceof MapValue) {
                sanitized.put(key, sanitizeNeo4jData(new JsonObject(((MapValue) value).asMap())));
            } else {
                sanitized.put(key, value);
            }
        }
        return sanitized;
    }

    private boolean isEmpty(Object str) {
        return str == null || "".equals(str) || "无".equals(str) || "不详".equals(str) || "遮挡".equals(str);
    }
}
